/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jpao.benchmark;

/**
 *
 * @author soa
 */
public class StopWatchMetric {

    /** the time in ns when the metric is taken. */
    private long time;
    /** the elapsed time in ns since the instanciation of the StopWatch that returned this metric. */
    private long elapsedTime;
    /** the label of this metric. Can be null, and in that case no label will be displayed when calling the toString method. */
    private String label;
    
    /** ns scale. */
    public static final long NANO_SECOND_SCALE = 1;
    /** µs scale. */
    public static final long MICRO_SECOND_SCALE = NANO_SECOND_SCALE * 1000;
    /** ms scale. */
    public static final long MILLI_SECOND_SCALE = MICRO_SECOND_SCALE * 1000;
    /** s scale. */
    public static final long SECOND_SCALE = MILLI_SECOND_SCALE * 1000;
    /** m scale. */
    public static final long MINUTE_SCALE = SECOND_SCALE * 60;
    /** h scale. */
    public static final long HOUR_SCALE = MINUTE_SCALE * 60;
    /** day scale. */
    public static final long DAY_SCALE = HOUR_SCALE * 24;

    /**
     * instanciates a new StopWatch Metric. Returned by the StopWatch class.
     * @param time the time in ns when the metric is taken.
     * @param elapsedTime the elapsed time in ns since the instanciation of the StopWatch that returned this metric.
     * @param label the label of this metric. Can be null, and in that case no label will be displayed when calling the toString method.
     */
    public StopWatchMetric(long time, long elapsedTime, String label) {
        this.time = time;
        this.elapsedTime = elapsedTime;
        this.label = label;
    }

    @Override
    public String toString() {

        // convert elapsed time in a more readable form
        StringBuilder elapsedTimeString = new StringBuilder();

        // only nano
        if (elapsedTime < MICRO_SECOND_SCALE) {
            elapsedTimeString.append(elapsedTime).append(" ns");

            // micro + nano
        } else if (elapsedTime < MILLI_SECOND_SCALE) {
            long micros = elapsedTime / MICRO_SECOND_SCALE;
            long nanos = elapsedTime % MICRO_SECOND_SCALE;
            elapsedTimeString.append(micros).append(" µs ").append(nanos).append(" ns");

            // milli + micro + nano
        } else if (elapsedTime < SECOND_SCALE) {
            long millis = elapsedTime / MILLI_SECOND_SCALE;
            long microsAndNanos = elapsedTime % MILLI_SECOND_SCALE;
            long micros = microsAndNanos / MICRO_SECOND_SCALE;
            long nanos = microsAndNanos % MICRO_SECOND_SCALE;
            elapsedTimeString.append(millis).append(" ms ").append(micros).append(" µs ").append(nanos).append(" ns");

            // sec + milli + micro + nano
        } else if (elapsedTime < MINUTE_SCALE) {
            long sec = elapsedTime / SECOND_SCALE;
            long millisAndMicrosAndNanos = elapsedTime % SECOND_SCALE;
            long millis = millisAndMicrosAndNanos / MILLI_SECOND_SCALE;
            long microsAndNanos = millisAndMicrosAndNanos % MILLI_SECOND_SCALE;
            long micros = microsAndNanos / MICRO_SECOND_SCALE;
            long nanos = microsAndNanos % MICRO_SECOND_SCALE;
            elapsedTimeString.append(sec).append(" s ").append(millis).append(" ms ").append(micros).append(" µs ").append(nanos).append(" ns");

            // min + sec + milli + micro + nano
        } else if (elapsedTime < HOUR_SCALE) {
            long min = elapsedTime / MINUTE_SCALE;
            long secAndMillisAndMicrosAndNanos = elapsedTime % MINUTE_SCALE;
            long sec = secAndMillisAndMicrosAndNanos / SECOND_SCALE;
            long millisAndMicrosAndNanos = secAndMillisAndMicrosAndNanos % SECOND_SCALE;
            long millis = millisAndMicrosAndNanos / MILLI_SECOND_SCALE;
            long microsAndNanos = millisAndMicrosAndNanos % MILLI_SECOND_SCALE;
            long micros = microsAndNanos / MICRO_SECOND_SCALE;
            long nanos = microsAndNanos % MICRO_SECOND_SCALE;
            elapsedTimeString.append(min).append(" m ").append(sec).append(" s ").append(millis).append(" ms ").append(micros).append(" µs ").append(nanos).append(" ns");

            // hour + min + sec + milli + micro + nano
        } else if (elapsedTime < DAY_SCALE) {
            long hour = elapsedTime / HOUR_SCALE;
            long minAndSecAndMillisAndMicrosAndNanos = elapsedTime % HOUR_SCALE;
            long min = minAndSecAndMillisAndMicrosAndNanos / MINUTE_SCALE;
            long minAndMillisAndMicrosAndNanos = minAndSecAndMillisAndMicrosAndNanos % MINUTE_SCALE;
            long sec = minAndMillisAndMicrosAndNanos / SECOND_SCALE;
            long millisAndMicrosAndNanos = minAndMillisAndMicrosAndNanos % SECOND_SCALE;
            long millis = millisAndMicrosAndNanos / MILLI_SECOND_SCALE;
            long microsAndNanos = millisAndMicrosAndNanos % MILLI_SECOND_SCALE;
            long micros = microsAndNanos / MICRO_SECOND_SCALE;
            long nanos = microsAndNanos % MICRO_SECOND_SCALE;
            elapsedTimeString.append(hour).append(" h ").append(min).append(" m ").append(sec).append(" s ").append(millis).append(" ms ").append(micros).append(" µs ").append(nanos).append(" ns");

        } else {
            long day = elapsedTime / DAY_SCALE;
            long hourAndMinAndSecAndMillisAndMicrosAndNanos = elapsedTime % DAY_SCALE;
            long hour = hourAndMinAndSecAndMillisAndMicrosAndNanos / HOUR_SCALE;
            long minAndSecAndMillisAndMicrosAndNanos = hourAndMinAndSecAndMillisAndMicrosAndNanos % HOUR_SCALE;
            long min = minAndSecAndMillisAndMicrosAndNanos / MINUTE_SCALE;
            long minAndMillisAndMicrosAndNanos = minAndSecAndMillisAndMicrosAndNanos % MINUTE_SCALE;
            long sec = minAndMillisAndMicrosAndNanos / SECOND_SCALE;
            long millisAndMicrosAndNanos = minAndMillisAndMicrosAndNanos % SECOND_SCALE;
            long millis = millisAndMicrosAndNanos / MILLI_SECOND_SCALE;
            long microsAndNanos = millisAndMicrosAndNanos % MILLI_SECOND_SCALE;
            long micros = microsAndNanos / MICRO_SECOND_SCALE;
            long nanos = microsAndNanos % MICRO_SECOND_SCALE;
            elapsedTimeString.append(day).append(" d ").append(hour).append(" h ").append(min).append(" m ").append(sec).append(" s ").append(millis).append(" ms ").append(micros).append(" µs ").append(nanos).append(" ns");
        }

        // build the corresponding string
        StringBuilder sb = new StringBuilder();
        if (label != null) {
            sb.append(label).append(" - ");
        }
        
        sb.append(elapsedTimeString.toString()).append(" (").append(time).append(')');
        return sb.toString();
    }

    public long getElapsedTime() {
        return elapsedTime;
    }

    public void setElapsedTime(long elapsedTime) {
        this.elapsedTime = elapsedTime;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }
}
